home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / lib / python2.5 / mhlib.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-29  |  34KB  |  1,261 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. """MH interface -- purely object-oriented (well, almost)
  5.  
  6. Executive summary:
  7.  
  8. import mhlib
  9.  
  10. mh = mhlib.MH()         # use default mailbox directory and profile
  11. mh = mhlib.MH(mailbox)  # override mailbox location (default from profile)
  12. mh = mhlib.MH(mailbox, profile) # override mailbox and profile
  13.  
  14. mh.error(format, ...)   # print error message -- can be overridden
  15. s = mh.getprofile(key)  # profile entry (None if not set)
  16. path = mh.getpath()     # mailbox pathname
  17. name = mh.getcontext()  # name of current folder
  18. mh.setcontext(name)     # set name of current folder
  19.  
  20. list = mh.listfolders() # names of top-level folders
  21. list = mh.listallfolders() # names of all folders, including subfolders
  22. list = mh.listsubfolders(name) # direct subfolders of given folder
  23. list = mh.listallsubfolders(name) # all subfolders of given folder
  24.  
  25. mh.makefolder(name)     # create new folder
  26. mh.deletefolder(name)   # delete folder -- must have no subfolders
  27.  
  28. f = mh.openfolder(name) # new open folder object
  29.  
  30. f.error(format, ...)    # same as mh.error(format, ...)
  31. path = f.getfullname()  # folder's full pathname
  32. path = f.getsequencesfilename() # full pathname of folder's sequences file
  33. path = f.getmessagefilename(n)  # full pathname of message n in folder
  34.  
  35. list = f.listmessages() # list of messages in folder (as numbers)
  36. n = f.getcurrent()      # get current message
  37. f.setcurrent(n)         # set current message
  38. list = f.parsesequence(seq)     # parse msgs syntax into list of messages
  39. n = f.getlast()         # get last message (0 if no messagse)
  40. f.setlast(n)            # set last message (internal use only)
  41.  
  42. dict = f.getsequences() # dictionary of sequences in folder {name: list}
  43. f.putsequences(dict)    # write sequences back to folder
  44.  
  45. f.createmessage(n, fp)  # add message from file f as number n
  46. f.removemessages(list)  # remove messages in list from folder
  47. f.refilemessages(list, tofolder) # move messages in list to other folder
  48. f.movemessage(n, tofolder, ton)  # move one message to a given destination
  49. f.copymessage(n, tofolder, ton)  # copy one message to a given destination
  50.  
  51. m = f.openmessage(n)    # new open message object (costs a file descriptor)
  52. m is a derived class of mimetools.Message(rfc822.Message), with:
  53. s = m.getheadertext()   # text of message's headers
  54. s = m.getheadertext(pred) # text of message's headers, filtered by pred
  55. s = m.getbodytext()     # text of message's body, decoded
  56. s = m.getbodytext(0)    # text of message's body, not decoded
  57. """
  58. MH_PROFILE = '~/.mh_profile'
  59. PATH = '~/Mail'
  60. MH_SEQUENCES = '.mh_sequences'
  61. FOLDER_PROTECT = 448
  62. import os
  63. import sys
  64. import re
  65. import mimetools
  66. import multifile
  67. import shutil
  68. from bisect import bisect
  69. __all__ = [
  70.     'MH',
  71.     'Error',
  72.     'Folder',
  73.     'Message']
  74.  
  75. class Error(Exception):
  76.     pass
  77.  
  78.  
  79. class MH:
  80.     '''Class representing a particular collection of folders.
  81.     Optional constructor arguments are the pathname for the directory
  82.     containing the collection, and the MH profile to use.
  83.     If either is omitted or empty a default is used; the default
  84.     directory is taken from the MH profile if it is specified there.'''
  85.     
  86.     def __init__(self, path = None, profile = None):
  87.         '''Constructor.'''
  88.         if profile is None:
  89.             profile = MH_PROFILE
  90.         
  91.         self.profile = os.path.expanduser(profile)
  92.         if path is None:
  93.             path = self.getprofile('Path')
  94.         
  95.         if not path:
  96.             path = PATH
  97.         
  98.         if not os.path.isabs(path) and path[0] != '~':
  99.             path = os.path.join('~', path)
  100.         
  101.         path = os.path.expanduser(path)
  102.         if not os.path.isdir(path):
  103.             raise Error, 'MH() path not found'
  104.         
  105.         self.path = path
  106.  
  107.     
  108.     def __repr__(self):
  109.         '''String representation.'''
  110.         return 'MH(%r, %r)' % (self.path, self.profile)
  111.  
  112.     
  113.     def error(self, msg, *args):
  114.         '''Routine to print an error.  May be overridden by a derived class.'''
  115.         sys.stderr.write('MH error: %s\n' % msg % args)
  116.  
  117.     
  118.     def getprofile(self, key):
  119.         '''Return a profile entry, None if not found.'''
  120.         return pickline(self.profile, key)
  121.  
  122.     
  123.     def getpath(self):
  124.         """Return the path (the name of the collection's directory)."""
  125.         return self.path
  126.  
  127.     
  128.     def getcontext(self):
  129.         '''Return the name of the current folder.'''
  130.         context = pickline(os.path.join(self.getpath(), 'context'), 'Current-Folder')
  131.         if not context:
  132.             context = 'inbox'
  133.         
  134.         return context
  135.  
  136.     
  137.     def setcontext(self, context):
  138.         '''Set the name of the current folder.'''
  139.         fn = os.path.join(self.getpath(), 'context')
  140.         f = open(fn, 'w')
  141.         f.write('Current-Folder: %s\n' % context)
  142.         f.close()
  143.  
  144.     
  145.     def listfolders(self):
  146.         '''Return the names of the top-level folders.'''
  147.         folders = []
  148.         path = self.getpath()
  149.         for name in os.listdir(path):
  150.             fullname = os.path.join(path, name)
  151.             if os.path.isdir(fullname):
  152.                 folders.append(name)
  153.                 continue
  154.         
  155.         folders.sort()
  156.         return folders
  157.  
  158.     
  159.     def listsubfolders(self, name):
  160.         '''Return the names of the subfolders in a given folder
  161.         (prefixed with the given folder name).'''
  162.         fullname = os.path.join(self.path, name)
  163.         nlinks = os.stat(fullname).st_nlink
  164.         if nlinks <= 2:
  165.             return []
  166.         
  167.         subfolders = []
  168.         subnames = os.listdir(fullname)
  169.         for subname in subnames:
  170.             fullsubname = os.path.join(fullname, subname)
  171.             if os.path.isdir(fullsubname):
  172.                 name_subname = os.path.join(name, subname)
  173.                 subfolders.append(name_subname)
  174.                 nlinks = nlinks - 1
  175.                 if nlinks <= 2:
  176.                     break
  177.                 
  178.             nlinks <= 2
  179.         
  180.         subfolders.sort()
  181.         return subfolders
  182.  
  183.     
  184.     def listallfolders(self):
  185.         '''Return the names of all folders and subfolders, recursively.'''
  186.         return self.listallsubfolders('')
  187.  
  188.     
  189.     def listallsubfolders(self, name):
  190.         '''Return the names of subfolders in a given folder, recursively.'''
  191.         fullname = os.path.join(self.path, name)
  192.         nlinks = os.stat(fullname).st_nlink
  193.         if nlinks <= 2:
  194.             return []
  195.         
  196.         subfolders = []
  197.         subnames = os.listdir(fullname)
  198.         for subname in subnames:
  199.             if subname[0] == ',' or isnumeric(subname):
  200.                 continue
  201.             
  202.             fullsubname = os.path.join(fullname, subname)
  203.             if os.path.isdir(fullsubname):
  204.                 name_subname = os.path.join(name, subname)
  205.                 subfolders.append(name_subname)
  206.                 if not os.path.islink(fullsubname):
  207.                     subsubfolders = self.listallsubfolders(name_subname)
  208.                     subfolders = subfolders + subsubfolders
  209.                 
  210.                 nlinks = nlinks - 1
  211.                 if nlinks <= 2:
  212.                     break
  213.                 
  214.             nlinks <= 2
  215.         
  216.         subfolders.sort()
  217.         return subfolders
  218.  
  219.     
  220.     def openfolder(self, name):
  221.         '''Return a new Folder object for the named folder.'''
  222.         return Folder(self, name)
  223.  
  224.     
  225.     def makefolder(self, name):
  226.         '''Create a new folder (or raise os.error if it cannot be created).'''
  227.         protect = pickline(self.profile, 'Folder-Protect')
  228.         if protect and isnumeric(protect):
  229.             mode = int(protect, 8)
  230.         else:
  231.             mode = FOLDER_PROTECT
  232.         os.mkdir(os.path.join(self.getpath(), name), mode)
  233.  
  234.     
  235.     def deletefolder(self, name):
  236.         '''Delete a folder.  This removes files in the folder but not
  237.         subdirectories.  Raise os.error if deleting the folder itself fails.'''
  238.         fullname = os.path.join(self.getpath(), name)
  239.         for subname in os.listdir(fullname):
  240.             fullsubname = os.path.join(fullname, subname)
  241.             
  242.             try:
  243.                 os.unlink(fullsubname)
  244.             continue
  245.             except os.error:
  246.                 self.error('%s not deleted, continuing...' % fullsubname)
  247.                 continue
  248.             
  249.  
  250.         
  251.         os.rmdir(fullname)
  252.  
  253.  
  254. numericprog = re.compile('^[1-9][0-9]*$')
  255.  
  256. def isnumeric(str):
  257.     return numericprog.match(str) is not None
  258.  
  259.  
  260. class Folder:
  261.     '''Class representing a particular folder.'''
  262.     
  263.     def __init__(self, mh, name):
  264.         '''Constructor.'''
  265.         self.mh = mh
  266.         self.name = name
  267.         if not os.path.isdir(self.getfullname()):
  268.             raise Error, 'no folder %s' % name
  269.         
  270.  
  271.     
  272.     def __repr__(self):
  273.         '''String representation.'''
  274.         return 'Folder(%r, %r)' % (self.mh, self.name)
  275.  
  276.     
  277.     def error(self, *args):
  278.         '''Error message handler.'''
  279.         self.mh.error(*args)
  280.  
  281.     
  282.     def getfullname(self):
  283.         '''Return the full pathname of the folder.'''
  284.         return os.path.join(self.mh.path, self.name)
  285.  
  286.     
  287.     def getsequencesfilename(self):
  288.         """Return the full pathname of the folder's sequences file."""
  289.         return os.path.join(self.getfullname(), MH_SEQUENCES)
  290.  
  291.     
  292.     def getmessagefilename(self, n):
  293.         '''Return the full pathname of a message in the folder.'''
  294.         return os.path.join(self.getfullname(), str(n))
  295.  
  296.     
  297.     def listsubfolders(self):
  298.         '''Return list of direct subfolders.'''
  299.         return self.mh.listsubfolders(self.name)
  300.  
  301.     
  302.     def listallsubfolders(self):
  303.         '''Return list of all subfolders.'''
  304.         return self.mh.listallsubfolders(self.name)
  305.  
  306.     
  307.     def listmessages(self):
  308.         '''Return the list of messages currently present in the folder.
  309.         As a side effect, set self.last to the last message (or 0).'''
  310.         messages = []
  311.         match = numericprog.match
  312.         append = messages.append
  313.         for name in os.listdir(self.getfullname()):
  314.             if match(name):
  315.                 append(name)
  316.                 continue
  317.         
  318.         messages = map(int, messages)
  319.         messages.sort()
  320.         if messages:
  321.             self.last = messages[-1]
  322.         else:
  323.             self.last = 0
  324.         return messages
  325.  
  326.     
  327.     def getsequences(self):
  328.         '''Return the set of sequences for the folder.'''
  329.         sequences = { }
  330.         fullname = self.getsequencesfilename()
  331.         
  332.         try:
  333.             f = open(fullname, 'r')
  334.         except IOError:
  335.             return sequences
  336.  
  337.         while None:
  338.             line = f.readline()
  339.             if not line:
  340.                 break
  341.             
  342.             fields = line.split(':')
  343.             if len(fields) != 2:
  344.                 self.error('bad sequence in %s: %s' % (fullname, line.strip()))
  345.             
  346.             key = fields[0].strip()
  347.             value = IntSet(fields[1].strip(), ' ').tolist()
  348.             sequences[key] = value
  349.             continue
  350.             return sequences
  351.  
  352.     
  353.     def putsequences(self, sequences):
  354.         '''Write the set of sequences back to the folder.'''
  355.         fullname = self.getsequencesfilename()
  356.         f = None
  357.         for key, seq in sequences.iteritems():
  358.             s = IntSet('', ' ')
  359.             s.fromlist(seq)
  360.             if not f:
  361.                 f = open(fullname, 'w')
  362.             
  363.             f.write('%s: %s\n' % (key, s.tostring()))
  364.         
  365.         if not f:
  366.             
  367.             try:
  368.                 os.unlink(fullname)
  369.             except os.error:
  370.                 pass
  371.             except:
  372.                 None<EXCEPTION MATCH>os.error
  373.             
  374.  
  375.         None<EXCEPTION MATCH>os.error
  376.         f.close()
  377.  
  378.     
  379.     def getcurrent(self):
  380.         '''Return the current message.  Raise Error when there is none.'''
  381.         seqs = self.getsequences()
  382.         
  383.         try:
  384.             return max(seqs['cur'])
  385.         except (ValueError, KeyError):
  386.             raise Error, 'no cur message'
  387.  
  388.  
  389.     
  390.     def setcurrent(self, n):
  391.         '''Set the current message.'''
  392.         updateline(self.getsequencesfilename(), 'cur', str(n), 0)
  393.  
  394.     
  395.     def parsesequence(self, seq):
  396.         '''Parse an MH sequence specification into a message list.
  397.         Attempt to mimic mh-sequence(5) as close as possible.
  398.         Also attempt to mimic observed behavior regarding which
  399.         conditions cause which error messages.'''
  400.         all = self.listmessages()
  401.         if not all:
  402.             raise Error, 'no messages in %s' % self.name
  403.         
  404.         if seq == 'all':
  405.             return all
  406.         
  407.         i = seq.find(':')
  408.         if i >= 0:
  409.             head = seq[:i]
  410.             dir = ''
  411.             tail = seq[i + 1:]
  412.             if tail[:1] in '-+':
  413.                 dir = tail[:1]
  414.                 tail = tail[1:]
  415.             
  416.             if not isnumeric(tail):
  417.                 raise Error, 'bad message list %s' % seq
  418.             
  419.             
  420.             try:
  421.                 count = int(tail)
  422.             except (ValueError, OverflowError):
  423.                 count = len(all)
  424.  
  425.             
  426.             try:
  427.                 anchor = self._parseindex(head, all)
  428.             except Error:
  429.                 msg = None
  430.                 seqs = self.getsequences()
  431.                 if head not in seqs:
  432.                     if not msg:
  433.                         msg = 'bad message list %s' % seq
  434.                     
  435.                     raise Error, msg, sys.exc_info()[2]
  436.                 
  437.                 msgs = seqs[head]
  438.                 if not msgs:
  439.                     raise Error, 'sequence %s empty' % head
  440.                 
  441.                 if dir == '-':
  442.                     return msgs[-count:]
  443.                 else:
  444.                     return msgs[:count]
  445.             except:
  446.                 dir == '-'
  447.  
  448.             if not dir:
  449.                 if head in ('prev', 'last'):
  450.                     dir = '-'
  451.                 
  452.             
  453.             if dir == '-':
  454.                 i = bisect(all, anchor)
  455.                 return all[max(0, i - count):i]
  456.             else:
  457.                 i = bisect(all, anchor - 1)
  458.                 return all[i:i + count]
  459.         
  460.         i = seq.find('-')
  461.         if i >= 0:
  462.             begin = self._parseindex(seq[:i], all)
  463.             end = self._parseindex(seq[i + 1:], all)
  464.             i = bisect(all, begin - 1)
  465.             j = bisect(all, end)
  466.             r = all[i:j]
  467.             if not r:
  468.                 raise Error, 'bad message list %s' % seq
  469.             
  470.             return r
  471.         
  472.         
  473.         try:
  474.             n = self._parseindex(seq, all)
  475.         except Error:
  476.             msg = None
  477.             seqs = self.getsequences()
  478.             if seq not in seqs:
  479.                 if not msg:
  480.                     msg = 'bad message list %s' % seq
  481.                 
  482.                 raise Error, msg
  483.             
  484.             return seqs[seq]
  485.  
  486.         if n not in all:
  487.             if isnumeric(seq):
  488.                 raise Error, "message %d doesn't exist" % n
  489.             else:
  490.                 raise Error, 'no %s message' % seq
  491.         else:
  492.             return [
  493.                 n]
  494.  
  495.     
  496.     def _parseindex(self, seq, all):
  497.         '''Internal: parse a message number (or cur, first, etc.).'''
  498.         if isnumeric(seq):
  499.             
  500.             try:
  501.                 return int(seq)
  502.             except (OverflowError, ValueError):
  503.                 return sys.maxint
  504.             except:
  505.                 None<EXCEPTION MATCH>(OverflowError, ValueError)
  506.             
  507.  
  508.         None<EXCEPTION MATCH>(OverflowError, ValueError)
  509.         if seq in ('cur', '.'):
  510.             return self.getcurrent()
  511.         
  512.         if seq == 'first':
  513.             return all[0]
  514.         
  515.         if seq == 'last':
  516.             return all[-1]
  517.         
  518.         if seq == 'next':
  519.             n = self.getcurrent()
  520.             i = bisect(all, n)
  521.             
  522.             try:
  523.                 return all[i]
  524.             except IndexError:
  525.                 raise Error, 'no next message'
  526.             except:
  527.                 None<EXCEPTION MATCH>IndexError
  528.             
  529.  
  530.         None<EXCEPTION MATCH>IndexError
  531.         if seq == 'prev':
  532.             n = self.getcurrent()
  533.             i = bisect(all, n - 1)
  534.             if i == 0:
  535.                 raise Error, 'no prev message'
  536.             
  537.             
  538.             try:
  539.                 return all[i - 1]
  540.             except IndexError:
  541.                 raise Error, 'no prev message'
  542.             except:
  543.                 None<EXCEPTION MATCH>IndexError
  544.             
  545.  
  546.         None<EXCEPTION MATCH>IndexError
  547.         raise Error, None
  548.  
  549.     
  550.     def openmessage(self, n):
  551.         '''Open a message -- returns a Message object.'''
  552.         return Message(self, n)
  553.  
  554.     
  555.     def removemessages(self, list):
  556.         '''Remove one or more messages -- may raise os.error.'''
  557.         errors = []
  558.         deleted = []
  559.         for n in list:
  560.             path = self.getmessagefilename(n)
  561.             commapath = self.getmessagefilename(',' + str(n))
  562.             
  563.             try:
  564.                 os.unlink(commapath)
  565.             except os.error:
  566.                 pass
  567.  
  568.             
  569.             try:
  570.                 os.rename(path, commapath)
  571.             except os.error:
  572.                 msg = None
  573.                 errors.append(msg)
  574.                 continue
  575.  
  576.             deleted.append(n)
  577.         
  578.         if deleted:
  579.             self.removefromallsequences(deleted)
  580.         
  581.         if errors:
  582.             if len(errors) == 1:
  583.                 raise os.error, errors[0]
  584.             else:
  585.                 raise os.error, ('multiple errors:', errors)
  586.         
  587.  
  588.     
  589.     def refilemessages(self, list, tofolder, keepsequences = 0):
  590.         """Refile one or more messages -- may raise os.error.
  591.         'tofolder' is an open folder object."""
  592.         errors = []
  593.         refiled = { }
  594.         for n in list:
  595.             ton = tofolder.getlast() + 1
  596.             path = self.getmessagefilename(n)
  597.             topath = tofolder.getmessagefilename(ton)
  598.             
  599.             try:
  600.                 os.rename(path, topath)
  601.             except os.error:
  602.                 
  603.                 try:
  604.                     shutil.copy2(path, topath)
  605.                     os.unlink(path)
  606.                 except (IOError, os.error):
  607.                     msg = None
  608.                     errors.append(msg)
  609.                     
  610.                     try:
  611.                         os.unlink(topath)
  612.                     continue
  613.                     except os.error:
  614.                         continue
  615.                     
  616.  
  617.                 except:
  618.                     None<EXCEPTION MATCH>(IOError, os.error)
  619.                 
  620.  
  621.                 None<EXCEPTION MATCH>(IOError, os.error)
  622.  
  623.             tofolder.setlast(ton)
  624.             refiled[n] = ton
  625.         
  626.         if refiled:
  627.             if keepsequences:
  628.                 tofolder._copysequences(self, refiled.items())
  629.             
  630.             self.removefromallsequences(refiled.keys())
  631.         
  632.         if errors:
  633.             if len(errors) == 1:
  634.                 raise os.error, errors[0]
  635.             else:
  636.                 raise os.error, ('multiple errors:', errors)
  637.         
  638.  
  639.     
  640.     def _copysequences(self, fromfolder, refileditems):
  641.         '''Helper for refilemessages() to copy sequences.'''
  642.         fromsequences = fromfolder.getsequences()
  643.         tosequences = self.getsequences()
  644.         changed = 0
  645.         for name, seq in fromsequences.items():
  646.             
  647.             try:
  648.                 toseq = tosequences[name]
  649.                 new = 0
  650.             except KeyError:
  651.                 toseq = []
  652.                 new = 1
  653.  
  654.             for fromn, ton in refileditems:
  655.                 if fromn in seq:
  656.                     toseq.append(ton)
  657.                     changed = 1
  658.                     continue
  659.             
  660.             if new and toseq:
  661.                 tosequences[name] = toseq
  662.                 continue
  663.         
  664.         if changed:
  665.             self.putsequences(tosequences)
  666.         
  667.  
  668.     
  669.     def movemessage(self, n, tofolder, ton):
  670.         '''Move one message over a specific destination message,
  671.         which may or may not already exist.'''
  672.         path = self.getmessagefilename(n)
  673.         f = open(path)
  674.         f.close()
  675.         del f
  676.         topath = tofolder.getmessagefilename(ton)
  677.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  678.         
  679.         try:
  680.             os.rename(topath, backuptopath)
  681.         except os.error:
  682.             pass
  683.  
  684.         
  685.         try:
  686.             os.rename(path, topath)
  687.         except os.error:
  688.             ok = 0
  689.             
  690.             try:
  691.                 tofolder.setlast(None)
  692.                 shutil.copy2(path, topath)
  693.                 ok = 1
  694.             finally:
  695.                 if not ok:
  696.                     
  697.                     try:
  698.                         os.unlink(topath)
  699.                     except os.error:
  700.                         pass
  701.                     except:
  702.                         None<EXCEPTION MATCH>os.error
  703.                     
  704.  
  705.  
  706.             os.unlink(path)
  707.  
  708.         self.removefromallsequences([
  709.             n])
  710.  
  711.     
  712.     def copymessage(self, n, tofolder, ton):
  713.         '''Copy one message over a specific destination message,
  714.         which may or may not already exist.'''
  715.         path = self.getmessagefilename(n)
  716.         f = open(path)
  717.         f.close()
  718.         del f
  719.         topath = tofolder.getmessagefilename(ton)
  720.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  721.         
  722.         try:
  723.             os.rename(topath, backuptopath)
  724.         except os.error:
  725.             pass
  726.  
  727.         ok = 0
  728.         
  729.         try:
  730.             tofolder.setlast(None)
  731.             shutil.copy2(path, topath)
  732.             ok = 1
  733.         finally:
  734.             if not ok:
  735.                 
  736.                 try:
  737.                     os.unlink(topath)
  738.                 except os.error:
  739.                     pass
  740.                 except:
  741.                     None<EXCEPTION MATCH>os.error
  742.                 
  743.  
  744.  
  745.  
  746.     
  747.     def createmessage(self, n, txt):
  748.         '''Create a message, with text from the open file txt.'''
  749.         path = self.getmessagefilename(n)
  750.         backuppath = self.getmessagefilename(',%d' % n)
  751.         
  752.         try:
  753.             os.rename(path, backuppath)
  754.         except os.error:
  755.             pass
  756.  
  757.         ok = 0
  758.         BUFSIZE = 16384
  759.         
  760.         try:
  761.             f = open(path, 'w')
  762.             while None:
  763.                 buf = txt.read(BUFSIZE)
  764.                 if not buf:
  765.                     break
  766.                 
  767.                 continue
  768.                 f.close()
  769.                 ok = 1
  770.             if not ok:
  771.                 
  772.                 try:
  773.                     os.unlink(path)
  774.                 except os.error:
  775.                     pass
  776.                 except:
  777.                     None<EXCEPTION MATCH>os.error
  778.                 
  779.  
  780.             return None
  781.  
  782.  
  783.     
  784.     def removefromallsequences(self, list):
  785.         """Remove one or more messages from all sequences (including last)
  786.         -- but not from 'cur'!!!"""
  787.         if hasattr(self, 'last') and self.last in list:
  788.             del self.last
  789.         
  790.         sequences = self.getsequences()
  791.         changed = 0
  792.         for name, seq in sequences.items():
  793.             if name == 'cur':
  794.                 continue
  795.             
  796.             for n in list:
  797.                 if n in seq:
  798.                     seq.remove(n)
  799.                     changed = 1
  800.                     if not seq:
  801.                         del sequences[name]
  802.                     
  803.                 seq
  804.             
  805.         
  806.         if changed:
  807.             self.putsequences(sequences)
  808.         
  809.  
  810.     
  811.     def getlast(self):
  812.         '''Return the last message number.'''
  813.         if not hasattr(self, 'last'):
  814.             self.listmessages()
  815.         
  816.         return self.last
  817.  
  818.     
  819.     def setlast(self, last):
  820.         '''Set the last message number.'''
  821.         if last is None:
  822.             if hasattr(self, 'last'):
  823.                 del self.last
  824.             
  825.         else:
  826.             self.last = last
  827.  
  828.  
  829.  
  830. class Message(mimetools.Message):
  831.     
  832.     def __init__(self, f, n, fp = None):
  833.         '''Constructor.'''
  834.         self.folder = f
  835.         self.number = n
  836.         if fp is None:
  837.             path = f.getmessagefilename(n)
  838.             fp = open(path, 'r')
  839.         
  840.         mimetools.Message.__init__(self, fp)
  841.  
  842.     
  843.     def __repr__(self):
  844.         '''String representation.'''
  845.         return 'Message(%s, %s)' % (repr(self.folder), self.number)
  846.  
  847.     
  848.     def getheadertext(self, pred = None):
  849.         """Return the message's header text as a string.  If an
  850.         argument is specified, it is used as a filter predicate to
  851.         decide which headers to return (its argument is the header
  852.         name converted to lower case)."""
  853.         if pred is None:
  854.             return ''.join(self.headers)
  855.         
  856.         headers = []
  857.         hit = 0
  858.         for line in self.headers:
  859.             if not line[0].isspace():
  860.                 i = line.find(':')
  861.                 if i > 0:
  862.                     hit = pred(line[:i].lower())
  863.                 
  864.             
  865.             if hit:
  866.                 headers.append(line)
  867.                 continue
  868.         
  869.         return ''.join(headers)
  870.  
  871.     
  872.     def getbodytext(self, decode = 1):
  873.         """Return the message's body text as string.  This undoes a
  874.         Content-Transfer-Encoding, but does not interpret other MIME
  875.         features (e.g. multipart messages).  To suppress decoding,
  876.         pass 0 as an argument."""
  877.         self.fp.seek(self.startofbody)
  878.         encoding = self.getencoding()
  879.         if not decode or encoding in ('', '7bit', '8bit', 'binary'):
  880.             return self.fp.read()
  881.         
  882.         
  883.         try:
  884.             StringIO = StringIO
  885.             import cStringIO
  886.         except ImportError:
  887.             StringIO = StringIO
  888.             import StringIO
  889.  
  890.         output = StringIO()
  891.         mimetools.decode(self.fp, output, encoding)
  892.         return output.getvalue()
  893.  
  894.     
  895.     def getbodyparts(self):
  896.         """Only for multipart messages: return the message's body as a
  897.         list of SubMessage objects.  Each submessage object behaves
  898.         (almost) as a Message object."""
  899.         if self.getmaintype() != 'multipart':
  900.             raise Error, 'Content-Type is not multipart/*'
  901.         
  902.         bdry = self.getparam('boundary')
  903.         if not bdry:
  904.             raise Error, 'multipart/* without boundary param'
  905.         
  906.         self.fp.seek(self.startofbody)
  907.         mf = multifile.MultiFile(self.fp)
  908.         mf.push(bdry)
  909.         parts = []
  910.         while mf.next():
  911.             n = '%s.%r' % (self.number, 1 + len(parts))
  912.             part = SubMessage(self.folder, n, mf)
  913.             parts.append(part)
  914.         mf.pop()
  915.         return parts
  916.  
  917.     
  918.     def getbody(self):
  919.         '''Return body, either a string or a list of messages.'''
  920.         if self.getmaintype() == 'multipart':
  921.             return self.getbodyparts()
  922.         else:
  923.             return self.getbodytext()
  924.  
  925.  
  926.  
  927. class SubMessage(Message):
  928.     
  929.     def __init__(self, f, n, fp):
  930.         '''Constructor.'''
  931.         Message.__init__(self, f, n, fp)
  932.         if self.getmaintype() == 'multipart':
  933.             self.body = Message.getbodyparts(self)
  934.         else:
  935.             self.body = Message.getbodytext(self)
  936.         self.bodyencoded = Message.getbodytext(self, decode = 0)
  937.  
  938.     
  939.     def __repr__(self):
  940.         '''String representation.'''
  941.         f = self.folder
  942.         n = self.number
  943.         fp = self.fp
  944.         return 'SubMessage(%s, %s, %s)' % (f, n, fp)
  945.  
  946.     
  947.     def getbodytext(self, decode = 1):
  948.         if not decode:
  949.             return self.bodyencoded
  950.         
  951.         if type(self.body) == type(''):
  952.             return self.body
  953.         
  954.  
  955.     
  956.     def getbodyparts(self):
  957.         if type(self.body) == type([]):
  958.             return self.body
  959.         
  960.  
  961.     
  962.     def getbody(self):
  963.         return self.body
  964.  
  965.  
  966.  
  967. class IntSet:
  968.     """Class implementing sets of integers.
  969.  
  970.     This is an efficient representation for sets consisting of several
  971.     continuous ranges, e.g. 1-100,200-400,402-1000 is represented
  972.     internally as a list of three pairs: [(1,100), (200,400),
  973.     (402,1000)].  The internal representation is always kept normalized.
  974.  
  975.     The constructor has up to three arguments:
  976.     - the string used to initialize the set (default ''),
  977.     - the separator between ranges (default ',')
  978.     - the separator between begin and end of a range (default '-')
  979.     The separators must be strings (not regexprs) and should be different.
  980.  
  981.     The tostring() function yields a string that can be passed to another
  982.     IntSet constructor; __repr__() is a valid IntSet constructor itself.
  983.     """
  984.     
  985.     def __init__(self, data = None, sep = ',', rng = '-'):
  986.         self.pairs = []
  987.         self.sep = sep
  988.         self.rng = rng
  989.         if data:
  990.             self.fromstring(data)
  991.         
  992.  
  993.     
  994.     def reset(self):
  995.         self.pairs = []
  996.  
  997.     
  998.     def __cmp__(self, other):
  999.         return cmp(self.pairs, other.pairs)
  1000.  
  1001.     
  1002.     def __hash__(self):
  1003.         return hash(self.pairs)
  1004.  
  1005.     
  1006.     def __repr__(self):
  1007.         return 'IntSet(%r, %r, %r)' % (self.tostring(), self.sep, self.rng)
  1008.  
  1009.     
  1010.     def normalize(self):
  1011.         self.pairs.sort()
  1012.         i = 1
  1013.         while i < len(self.pairs):
  1014.             (alo, ahi) = self.pairs[i - 1]
  1015.             (blo, bhi) = self.pairs[i]
  1016.             if ahi >= blo - 1:
  1017.                 self.pairs[i - 1:i + 1] = [
  1018.                     (alo, max(ahi, bhi))]
  1019.                 continue
  1020.             i = i + 1
  1021.  
  1022.     
  1023.     def tostring(self):
  1024.         s = ''
  1025.         for lo, hi in self.pairs:
  1026.             if lo == hi:
  1027.                 t = repr(lo)
  1028.             else:
  1029.                 t = repr(lo) + self.rng + repr(hi)
  1030.             if s:
  1031.                 s = s + self.sep + t
  1032.                 continue
  1033.             s = t
  1034.         
  1035.         return s
  1036.  
  1037.     
  1038.     def tolist(self):
  1039.         l = []
  1040.         for lo, hi in self.pairs:
  1041.             m = range(lo, hi + 1)
  1042.             l = l + m
  1043.         
  1044.         return l
  1045.  
  1046.     
  1047.     def fromlist(self, list):
  1048.         for i in list:
  1049.             self.append(i)
  1050.         
  1051.  
  1052.     
  1053.     def clone(self):
  1054.         new = IntSet()
  1055.         new.pairs = self.pairs[:]
  1056.         return new
  1057.  
  1058.     
  1059.     def min(self):
  1060.         return self.pairs[0][0]
  1061.  
  1062.     
  1063.     def max(self):
  1064.         return self.pairs[-1][-1]
  1065.  
  1066.     
  1067.     def contains(self, x):
  1068.         for lo, hi in self.pairs:
  1069.             if x <= x:
  1070.                 pass
  1071.             elif x <= hi:
  1072.                 return True
  1073.                 continue
  1074.         
  1075.         return False
  1076.  
  1077.     
  1078.     def append(self, x):
  1079.         for i in range(len(self.pairs)):
  1080.             (lo, hi) = self.pairs[i]
  1081.             if x < lo:
  1082.                 if x + 1 == lo:
  1083.                     self.pairs[i] = (x, hi)
  1084.                 else:
  1085.                     self.pairs.insert(i, (x, x))
  1086.                 if i > 0 and x - 1 == self.pairs[i - 1][1]:
  1087.                     self.pairs[i - 1:i + 1] = [
  1088.                         (self.pairs[i - 1][0], self.pairs[i][1])]
  1089.                 
  1090.                 return None
  1091.             
  1092.             if x <= hi:
  1093.                 return None
  1094.                 continue
  1095.         
  1096.         i = len(self.pairs) - 1
  1097.         if i >= 0:
  1098.             (lo, hi) = self.pairs[i]
  1099.             if x - 1 == hi:
  1100.                 self.pairs[i] = (lo, x)
  1101.                 return None
  1102.             
  1103.         
  1104.         self.pairs.append((x, x))
  1105.  
  1106.     
  1107.     def addpair(self, xlo, xhi):
  1108.         if xlo > xhi:
  1109.             return None
  1110.         
  1111.         self.pairs.append((xlo, xhi))
  1112.         self.normalize()
  1113.  
  1114.     
  1115.     def fromstring(self, data):
  1116.         new = []
  1117.         for part in data.split(self.sep):
  1118.             list = []
  1119.             for subp in part.split(self.rng):
  1120.                 s = subp.strip()
  1121.                 list.append(int(s))
  1122.             
  1123.             if len(list) == 1:
  1124.                 new.append((list[0], list[0]))
  1125.                 continue
  1126.             if len(list) == 2 and list[0] <= list[1]:
  1127.                 new.append((list[0], list[1]))
  1128.                 continue
  1129.             raise ValueError, 'bad data passed to IntSet'
  1130.         
  1131.         self.pairs = self.pairs + new
  1132.         self.normalize()
  1133.  
  1134.  
  1135.  
  1136. def pickline(file, key, casefold = 1):
  1137.     
  1138.     try:
  1139.         f = open(file, 'r')
  1140.     except IOError:
  1141.         return None
  1142.  
  1143.     pat = re.escape(key) + ':'
  1144.     if casefold:
  1145.         pass
  1146.     prog = re.compile(pat, re.IGNORECASE)
  1147.     while None:
  1148.         line = f.readline()
  1149.         if not line:
  1150.             break
  1151.         
  1152.         if prog.match(line):
  1153.             text = line[len(key) + 1:]
  1154.             while None:
  1155.                 line = f.readline()
  1156.                 if not line or not line[0].isspace():
  1157.                     break
  1158.                 
  1159.                 text = text + line
  1160.                 continue
  1161.                 return text.strip()
  1162.                 continue
  1163.                 continue
  1164.                 return None
  1165.  
  1166.  
  1167. def updateline(file, key, value, casefold = 1):
  1168.     
  1169.     try:
  1170.         f = open(file, 'r')
  1171.         lines = f.readlines()
  1172.         f.close()
  1173.     except IOError:
  1174.         lines = []
  1175.  
  1176.     pat = re.escape(key) + ':(.*)\n'
  1177.     if casefold:
  1178.         pass
  1179.     prog = re.compile(pat, re.IGNORECASE)
  1180.     if value is None:
  1181.         newline = None
  1182.     else:
  1183.         newline = '%s: %s\n' % (key, value)
  1184.     for i in range(len(lines)):
  1185.         line = lines[i]
  1186.         if prog.match(line):
  1187.             if newline is None:
  1188.                 del lines[i]
  1189.             else:
  1190.                 lines[i] = newline
  1191.             break
  1192.             continue
  1193.     elif newline is not None:
  1194.         lines.append(newline)
  1195.     
  1196.     tempfile = file + '~'
  1197.     f = open(tempfile, 'w')
  1198.     for line in lines:
  1199.         f.write(line)
  1200.     
  1201.     f.close()
  1202.     os.rename(tempfile, file)
  1203.  
  1204.  
  1205. def test():
  1206.     global mh, f
  1207.     os.system('rm -rf $HOME/Mail/@test')
  1208.     mh = MH()
  1209.     
  1210.     def do(s):
  1211.         print s
  1212.         print eval(s)
  1213.  
  1214.     do('mh.listfolders()')
  1215.     do('mh.listallfolders()')
  1216.     testfolders = [
  1217.         '@test',
  1218.         '@test/test1',
  1219.         '@test/test2',
  1220.         '@test/test1/test11',
  1221.         '@test/test1/test12',
  1222.         '@test/test1/test11/test111']
  1223.     for t in testfolders:
  1224.         do('mh.makefolder(%r)' % (t,))
  1225.     
  1226.     do("mh.listsubfolders('@test')")
  1227.     do("mh.listallsubfolders('@test')")
  1228.     f = mh.openfolder('@test')
  1229.     do('f.listsubfolders()')
  1230.     do('f.listallsubfolders()')
  1231.     do('f.getsequences()')
  1232.     seqs = f.getsequences()
  1233.     seqs['foo'] = IntSet('1-10 12-20', ' ').tolist()
  1234.     print seqs
  1235.     f.putsequences(seqs)
  1236.     do('f.getsequences()')
  1237.     for t in reversed(testfolders):
  1238.         do('mh.deletefolder(%r)' % (t,))
  1239.     
  1240.     do('mh.getcontext()')
  1241.     context = mh.getcontext()
  1242.     f = mh.openfolder(context)
  1243.     do('f.getcurrent()')
  1244.     for seq in ('first', 'last', 'cur', '.', 'prev', 'next', 'first:3', 'last:3', 'cur:3', 'cur:-3', 'prev:3', 'next:3', '1:3', '1:-3', '100:3', '100:-3', '10000:3', '10000:-3', 'all'):
  1245.         
  1246.         try:
  1247.             do('f.parsesequence(%r)' % (seq,))
  1248.         except Error:
  1249.             msg = None
  1250.             print 'Error:', msg
  1251.  
  1252.         stuff = os.popen('pick %r 2>/dev/null' % (seq,)).read()
  1253.         list = map(int, stuff.split())
  1254.         print list, '<-- pick'
  1255.     
  1256.     do('f.listmessages()')
  1257.  
  1258. if __name__ == '__main__':
  1259.     test()
  1260.  
  1261.